home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Snippets / MultiWin TE⁄Undo⁄S_H 1.0.1 / (Original) Show_help2.0 ƒ / Show_help.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-08  |  34.8 KB  |  562 lines  |  [TEXT/KAHL]

  1. #define USE_PICTS    1    /* 1 if PICTs are to be displayed, 0 for text only */
  2. #define ONE_RESOURCE 1    /* 1 for Get1Resource, 0 for GetResource, etc. */
  3. #define COMPRESSION    1    /* compressed TEXT/styl or PICTs */
  4. #define STANDALONE    1
  5.  
  6. #define CHECKPOINTS 0
  7. #define ASSERTIONS    0
  8.  
  9. #define    PREFLIGHT_MEMORY    20000L
  10.  
  11. #if ASSERTIONS
  12.     #define    ASSERT_SET_NIL(lvalue)    lvalue = NIL
  13. #else
  14.     #define    ASSERT_SET_NIL(lvalue)
  15. #endif
  16.  
  17. #if STANDALONE
  18.     #define        Show_help    main
  19. #else
  20.     void    main( void );
  21. #endif
  22.  
  23. #include "Show_help.h"
  24. #include "Show_help typedefs.h"
  25.  
  26. /*
  27.     Show_help by James W. Walker, June 1991
  28.     
  29.     version 2.0, updated July 1992
  30.     
  31.     This code is freely usable.  If you want to show your gratitude,
  32.     you could send me a free copy of whatever program you develop
  33.     with it.
  34.     
  35.     e-mail:
  36.         Internet            76367.2271@compuserve.com
  37.         CIS                    76367,2271
  38.         America Online        JWWalker
  39.     
  40.     This code displays scrolling text in a dialog box.  The text comes
  41.     from TEXT/styl resources, which can be created with ResEdit 2.1 or
  42.     with an accompanying HyperCard stack.
  43.     The text cannot be edited, but one can select text and copy it to
  44.     the clipboard using command-C, or save it as a TeachText file.
  45.     
  46.     Pictures can be included in the text using the same scheme as
  47.     TeachText: Each option-space character indicates where the top
  48.     edge of a picture should go, and pictures are centered horizontally.
  49.     Pictures come from consecutively-numbered PICT resources.  
  50.     
  51.     A popup menu can be used to jump to "bookmarks", which are indicated
  52.     by tab characters at ends of lines.
  53.     
  54.     Prototype:
  55.     
  56.     pascal void Show_help( short help_info_id,
  57.                 pascal void (*Handle_update)( WindowPtr ) );
  58.         
  59.     TO DO: error recovery, support for modeless use.
  60. */
  61.  
  62.  
  63.  
  64. #ifndef NIL
  65.     #define        NIL        0L
  66. #endif
  67.  
  68. #if ONE_RESOURCE
  69.     #define GetResource            Get1Resource
  70.     #define CountResources        Count1Resources
  71.     #define GetNamedResource    Get1NamedResource
  72. #endif ONE_RESOURCE
  73.  
  74. #if ASSERTIONS
  75.     #define        ASSERT(x,y)        if (!(x)) {DebugStr("\p" y);}
  76. #else
  77.     #define        ASSERT(x,y)
  78. #endif ASSERTIONS
  79.  
  80. #if CHECKPOINTS
  81.     #define CKPT(x)        DebugStr( "\p" x )
  82. #else
  83.     #define CKPT(x)
  84. #endif CHECKPOINTS
  85.  
  86. enum {
  87.     c_OK = 1,    /* OK button */
  88.     c_help,        /* userItem for our help display */
  89.     c_save,        /* Button to save as TeachText */
  90.     c_menu        /* userItem for popup menu */
  91. };
  92.  
  93. #define        SCROLLBAR_WIDTH    16
  94. #define        TEXT_INSET        4
  95.  
  96. #define        INITIAL_HIGHLIGHTS        8
  97.  
  98.  
  99.  
  100. /* Prototypes of private routines
  101. */
  102. static pascal Boolean Help_filter( DialogPtr dialog,
  103.     EventRecord    *event, short *itemHit);
  104. static pascal void  Text_userItem_proc( WindowPtr the_window, short item_num );
  105. static pascal void  Menu_userItem_proc( WindowPtr the_window, short item_num );
  106. static pascal void Scroll_text( ControlHandle the_bar, short part_code );
  107. static pascal Auto_scroll( void );
  108. static void Handle_scroll( DialogPtr dialog,
  109.     short the_part, Point local_point );
  110. static void Adjust_text( DialogPtr dialog );
  111. static void Save_text( TEHandle the_text, StringPtr default_filename );
  112. static void Topic_menu( DialogPtr dlog, MenuHandle help_popup );
  113. static MenuHandle Build_popup( TEHandle    the_text, StringPtr default_menuname );
  114. static short    Find_char(
  115.             Handle    data_h,        // handle to a block of characters
  116.             short    offset,        // initial offset within block
  117.             char    what );        // the character we're looking for
  118.  
  119. #if COMPRESSION
  120.     static Handle Get_compressed_resource( ResType    the_type, short the_ID );
  121.     static void Release_compressed_resource( Handle rsrc_h );
  122.     static void Dispose_compressed_data( Handle the_handle );
  123. #else
  124.     #define Get_compressed_resource            GetResource
  125.     #define Release_compressed_resource        ReleaseResource
  126.     #define    Dispose_compressed_data(x)
  127. #endif
  128.  
  129. #if USE_PICTS
  130. static void Find_pictures( DialogPtr dlog, short first_pict_id );
  131. static void Draw_picts( WindowPtr the_window, Rect *update_rect );
  132. static pascal void High_hook( Rect *high_rect );
  133. static void Do_deferred_hilites( help_ptr  hptr, Rect *update_rect );
  134. static void Push_highlight( high_info **hh, Rect *rect );
  135. static Boolean Pop_highlight( high_info **hh, Rect *rect );
  136. #endif
  137.  
  138. /* ------------------------- Show_help ---------------------);
  139.     }
  140.     asm {
  141.         movem.L        (SP)+, a1-a5/d1-d7
  142.         moveQ        #1, D0
  143.     }
  144. }
  145.  
  146. #if USE_PICTS
  147. /* ------------------------- Draw_picts --------------------------------- */
  148. /*
  149.     Called by Adjust_text and Text_userItem_proc to draw pictures.
  150. */
  151. static void Draw_picts( WindowPtr the_window, Rect *update_rect )
  152. {
  153.     register TEHandle    the_text;
  154.     register short        pict_count, pict_index;
  155.     PicHandle    the_pict;
  156.     short         v_offset;
  157.     Rect        pict_loc, dummy;
  158.     
  159.     CKPT( "Draw_picts");
  160.     the_text = (TEHandle) GetWRefCon( the_window );
  161.     v_offset = (**the_text).destRect.top - (**the_text).viewRect.top
  162.         - TEXT_INSET;
  163.     pict_count = ((help_ptr) the_window)->pict_count;
  164.     for (pict_index = 0; pict_index < pict_count; pict_index++)
  165.     {
  166.         pict_loc = ((help_ptr) the_window)->pict_data[pict_index].bounds;
  167.         OffsetRect( &pict_loc, 0, v_offset );
  168.         if (!SectRect( &pict_loc, update_rect, &dummy ))
  169.             continue;
  170.         the_pict = ((help_ptr) the_window)->pict_data[pict_index].pict;
  171.         GetClip( ((help_ptr) the_window)->save_clip );
  172.         ClipRect( update_rect );
  173.         DrawPicture( the_pict, &pict_loc );
  174.         SetClip( ((help_ptr) the_window)->save_clip );
  175.     }
  176. }
  177.  
  178. #define        OPTION_SPACE_CHAR    0xCA
  179.  
  180. /* ---------------------- Find_pictures ---------------------------- */
  181. static void Find_pictures( DialogPtr dlog, short first_pict_id )
  182. {
  183.     register TEHandle    the_text;
  184.     Handle        text_h;
  185.     SignedByte    text_state;
  186.     register short        offset;
  187.     short        num_picts;
  188.     register short        which_pict;
  189.     pict_info    *pict;
  190.     Point        place;
  191.     short        line_height, font_ascent;
  192.     TextStyle    what_style;
  193.     
  194.     CKPT( "Find_pictures");
  195.     the_text = (TEHandle) GetWRefCon( dlog );
  196.     text_h = (**the_text).hText;
  197.     text_state = HGetState( text_h );
  198.     HLock( text_h );
  199.     
  200.     /* Count option-space characters in the text. */
  201.     offset = 0;
  202.     num_picts = 0;
  203.     offset = Find_char( text_h, offset, OPTION_SPACE_CHAR );
  204.     while ( offset >= 0 )
  205.     {
  206.         num_picts++;
  207.         offset++;
  208.         offset = Find_char( text_h, offset, OPTION_SPACE_CHAR );
  209.     }
  210.     
  211.     /* Allocate storage for an array of picture bounds. */
  212.     pict = (pict_info *) NewPtr( sizeof(pict_info) * num_picts );
  213.     ASSERT( pict != NIL, "NewPtr failed for pict_data" );
  214.     ((help_ptr)dlog)->pict_data = pict;
  215.     
  216.     /*
  217.         Initialize the picture info.  For each picture we record the
  218.         picture handle and its rectangle, in unscrolled window
  219.         coordinates.
  220.     */
  221.     offset = 0;
  222.     for (which_pict = 0; which_pict < num_picts; which_pict++)
  223.     {
  224.         pict[which_pict].pict = (PicHandle) Get_compressed_resource( 'PICT',
  225.             first_pict_id + which_pict );
  226.         if ( pict[which_pict].pict == NIL )
  227.             break;
  228.         offset = Find_char( text_h, offset, OPTION_SPACE_CHAR );
  229.         place = TEGetPoint( offset, the_text );
  230.         TEGetStyle( offset, &what_style, &line_height,
  231.             &font_ascent, the_text );
  232.         place.v -= line_height;    /* align picture with TOP of option-space */
  233.         offset++;
  234.         pict[which_pict].bounds = (**pict[which_pict].pict).picFrame;
  235.         OffsetRect( &pict[which_pict].bounds,
  236.             ( ((**the_text).destRect.right + (**the_text).destRect.left) -
  237.             (pict[which_pict].bounds.right + pict[which_pict].bounds.left)
  238.             ) / 2,
  239.             - pict[which_pict].bounds.top + place.v );
  240.     }
  241.     ((help_ptr)dlog)->pict_count = which_pict;
  242.     
  243. getout:
  244.     HSetState( text_h, text_state );
  245. }
  246. #endif /* USE_PICTS */
  247.  
  248. /* ---------------------- Scroll_text ---------------------------- */
  249. /*
  250.     This is used as a TrackControl actionProc for scrolling, and also
  251.     called by Auto_scroll for automatic scrolling.
  252. */
  253. static pascal void Scroll_text( ControlHandle the_bar, short part_code )
  254. {
  255.     register TEHandle    the_text;
  256.     register short        delta;
  257.     register WindowPtr    the_display;
  258.     short                old_value;
  259.     short                offset, line;
  260.     Point                place;
  261.     Rect                view;
  262.     TextStyle            style;
  263.     short                line_height, font_ascent;
  264.     
  265.     CKPT( "Scroll_text");
  266.     if (part_code != 0)
  267.     {
  268.         the_display = (**the_bar).contrlOwner;
  269.         the_text = (TEHandle) GetWRefCon( the_display );
  270.         view = (**the_text).viewRect;
  271.         place.h = view.left + TEXT_INSET;
  272.         
  273.         switch (part_code)
  274.         {
  275.             case inUpButton:
  276.                 place.v = view.top;
  277.                 /*
  278.                     If we get the offset of the left edge of the top line,
  279.                     then subtract 1, we should have an offset belonging
  280.                     to the previous line.
  281.                 */
  282.                 offset = TEGetOffset( place, the_text ) - 1;
  283.                 if (offset == -1) offset = 0;
  284.                 place = TEGetPoint( offset, the_text );
  285.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  286.                     the_text );
  287.                 delta = place.v - line_height - view.top;
  288.                 break;
  289.             case inDownButton:
  290.                 place.v = view.bottom + 2;
  291.                 offset = TEGetOffset( place, the_text );
  292.                 place = TEGetPoint( offset, the_text );
  293.                 /* Now place.v is at the baseline of the border line. */
  294.                 delta = place.v - view.bottom;
  295.                 break;
  296.             case inPageUp:
  297.                 /*
  298.                     I want top border line to remain visible, and
  299.                     the top of a line should end up at view.top.
  300.                 */
  301.                 place.v = view.top + 2;
  302.                 offset = TEGetOffset( place, the_text );
  303.                 place = TEGetPoint( offset, the_text );
  304.                 /* place.v is at the baseline of the top border line. */
  305.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  306.                     the_text );
  307.                 place.v += line_height - font_ascent;
  308.                 place.v -= view.bottom - view.top;
  309.                 offset = TEGetOffset( place, the_text );
  310.                 place = TEGetPoint( offset, the_text );
  311.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  312.                     the_text );
  313.                 delta = place.v - view.top;
  314.                 if (offset == 0)
  315.                     delta -= line_height;
  316.                 break;
  317.             case inPageDown:
  318.                 /*
  319.                     I want bottom border line to remain visible, and
  320.                     the bottom of a line should end up at view.bottom.
  321.                 */
  322.                 place.v = view.bottom - 2;
  323.                 offset = TEGetOffset( place, the_text );
  324.                 place = TEGetPoint( offset, the_text );
  325.                 /* place.v is at the baseline of the bottom border line. */
  326.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  327.                     the_text );
  328.                 place.v -= font_ascent; /* Top edge of bottom border line */
  329.                 place.v += view.bottom - view.top;
  330.                 /* We're looking at the bottom border of the next page. */
  331.                 offset = TEGetOffset( place, the_text );
  332.                 place = TEGetPoint( offset, the_text );
  333.                 TEGetStyle( offset, &style, &line_height, &font_ascent,
  334.                     the_text );
  335.                 delta =  place.v - line_height - view.bottom;
  336.                 if (offset == (**the_text).teLength)
  337.                     delta += line_height;
  338.                 break;
  339.         }
  340.         old_value = GetCtlValue( the_bar );
  341.         if ( ((delta < 0) && (old_value > 0)) ||
  342.             ((delta > 0) && (old_value < GetCtlMax(the_bar))) )
  343.         {
  344.             /*
  345.                 When this routine is called, TextEdit may have set the
  346.                 clipping region to the view rectangle, so we reset it
  347.                 here to make sure the scroll bar gets drawn.
  348.             */
  349.             GetClip( ((help_ptr) the_display)->save_clip );
  350.             ClipRect( &the_display->portRect );
  351.             SetCtlValue( the_bar, old_value + delta );
  352.             SetClip( ((help_ptr) the_display)->save_clip );
  353.         }
  354.         Adjust_text( the_display );
  355.     }
  356. }
  357.  
  358. /* ---------------------- Adjust_text ---------------------------- */
  359. /*
  360.     Called by Handle_scroll and Scroll_text to scroll the text and
  361.     pictures into sync with the scroll bar's control value.
  362. */
  363. static void Adjust_text( DialogPtr    dialog )
  364. {
  365.     register    TEHandle    the_text;
  366.     register    short    scroll_down;
  367.     short            old_scroll;
  368.     Rect            update_rect;
  369.     
  370.     CKPT( "Adjust_text");
  371.     the_text = (TEHandle) GetWRefCon( dialog );
  372.     old_scroll = (**the_text).viewRect.top - (**the_text).destRect.top;
  373.     scroll_down = old_scroll -
  374.         GetCtlValue( ((help_ptr) dialog)->scrollbar );
  375.     if (scroll_down == 0)
  376.         return;
  377. #if USE_PICTS
  378.     ((help_ptr) dialog)->high_defer_flag = true;
  379.     //((help_ptr) dialog)->high_waiting = 0;
  380. #endif
  381.     TEScroll( 0, scroll_down, the_text );
  382. #if USE_PICTS
  383.     update_rect = (**the_text).viewRect;
  384.     if (scroll_down > 0)
  385.     {
  386.         if (scroll_down < (update_rect.bottom - update_rect.top))
  387.             update_rect.bottom = update_rect.top + scroll_down;
  388.     }
  389.     else
  390.         if (- scroll_down < (update_rect.bottom - update_rect.top))
  391.             update_rect.top = update_rect.bottom + scroll_down;
  392.     Draw_picts( dialog, &update_rect );
  393.     Do_deferred_hilites( (help_ptr) dialog, &update_rect );
  394. #endif
  395. }
  396.  
  397.  
  398. /* ---------------------- Handle_scroll ---------------------------- */
  399. /*
  400.     Called by Help_filter to handle mouseDown events in the scroll bar.
  401. */
  402. static void Handle_scroll( DialogPtr dialog, short the_part, Point where )
  403. {
  404.     register    ControlHandle the_bar;
  405.     
  406.     CKPT( "Handle_scroll"); SetPort( dialog );
  407.     the_bar = ((help_ptr) dialog)->scrollbar;
  408.     if (the_part == inThumb)
  409.     {
  410.         (void) TrackControl( the_bar, where, NIL );
  411.         Adjust_text( dialog );
  412.     }
  413.     else
  414.         (void) TrackControl( the_bar, where, (ProcPtr)Scroll_text );
  415.     
  416. }
  417.  
  418. /* ---------------------- Help_filter ------------------------- */
  419. /*
  420.     This is the dialog event filter for our help window.
  421. */
  422. #define        RETURN_CHAR        0x0D
  423. #define        TILDE_CHAR        0x7E
  424. #define        ENTER_CHAR        0x03
  425. #define        ESCAPE_CHAR        0x1B
  426.  
  427. static pascal Boolean Help_filter( DialogPtr dialog,
  428.     EventRecord    *event, short *itemHit)
  429. {
  430.     Point    local_point;
  431.     short    the_part;
  432.     ControlHandle    the_control;
  433.     short    charcode;
  434.     register TEHandle    the_text;
  435.     Rect    item_box;
  436.     short    cursor;
  437.     WindowPtr    which_window;
  438.     Boolean    retval;
  439.     RgnHandle    gray_rgn;
  440.     Rect        gray_rect;
  441.     
  442.     retval = false; // usually, let the Dialog Mgr do further processing
  443.     the_text = (TEHandle) GetWRefCon( dialog );
  444.  
  445.     GetMouse( &local_point );
  446.     if (PtInRect( local_point, &(**the_text).viewRect ))
  447.         SetCursor( *(((help_ptr) dialog)->ibeam_cursor) );
  448.     else
  449.         InitCursor();
  450.     TEIdle( the_text );
  451.     
  452.     switch (event->what) {
  453.         case nullEvent:
  454.             break;
  455.         case updateEvt:
  456.             which_window = (WindowPtr) event->message;
  457.             if ( (which_window != dialog) &&
  458.                 (((help_ptr)dialog)->Handle_update != NIL) )
  459.             {
  460.                 (((help_ptr)dialog)->Handle_update)( which_window );
  461.                 retval = true;    // done with this event
  462.             }
  463.             break;
  464.         case mouseDown:
  465.             CKPT( "Help_filter mousedown");
  466.             the_part = FindWindow( event->where, &which_window );
  467.             if (the_part == inMenuBar)
  468.             {
  469.                 /*
  470.                     Calling MenuSelect lets one use the help and
  471.                     application menus.
  472.                 */
  473.                 (void) MenuSelect( event->where );
  474.                 retval = true;    // get rid of the beep
  475.             }
  476.             else if ( (which_window == dialog) && (the_part == inContent) )
  477.             {
  478.                 local_point = event->where;
  479.                 GlobalToLocal( &local_point );
  480.                 the_part = FindControl( local_point, dialog, &the_control );
  481.                 if (the_part && ((**the_control).contrlMax > 1) )
  482.                 {
  483.                     Handle_scroll( dialog, the_part, local_point );    
  484.                     *itemHit = 2;
  485.                     retval = true;
  486.                     break;
  487.                 }
  488.                 else if (PtInRect( local_point, &(**the_text).viewRect ))
  489.                 {
  490.                     if (event->modifiers & shiftKey)
  491.                         TEClick( local_point, true, the_text );
  492.                     else
  493.                         TEClick( local_point, false, the_text );
  494.                     retval = true;
  495.                 }
  496.             }
  497.             else if ( (which_window == dialog) && (the_part == inDrag) )
  498.             {
  499.                 gray_rgn = GetGrayRgn();
  500.                 gray_rect = (**gray_rgn).rgnBBox;
  501.                 DragWindow( dialog, event->where, &gray_rect );
  502.                 retval = true;    // no beep
  503.             }
  504.             break;
  505.         case keyDown :    
  506.         case autoKey :
  507.             charcode = event->message & charCodeMask;
  508.             /*
  509.                 There's no Cancel button, so we treat the OK button
  510.                 the same as a Cancel button.
  511.             */
  512.             if ( (charcode == RETURN_CHAR) || (charcode == ENTER_CHAR) ||
  513.                 (charcode == TILDE_CHAR) || (charcode == ESCAPE_CHAR) ||
  514.                 ((charcode == '.') && (event->modifiers & cmdKey)) )
  515.             {
  516.                 *itemHit = c_OK;    /* OK */
  517.                 Flash_button( dialog, *itemHit );
  518.                 retval = TRUE;
  519.             }
  520.             else if ( (charcode == 'c') && (event->modifiers & cmdKey) )
  521.             {
  522.                 (void) ZeroScrap();
  523.                 TECopy( the_text );
  524.                 SystemEdit(3);
  525.                 local_point = (**the_text).selPoint;
  526.                 *(long *)&local_point = PinRect( &(**the_text).viewRect,
  527.                     local_point );
  528.                 cursor = TEGetOffset( local_point, the_text );
  529.                 TESetSelect( (long)cursor, (long)cursor, the_text );
  530.                 event->what = nullEvent;
  531.                 retval = true;
  532.             }
  533.             break;
  534.     } /* end switch */
  535.     
  536.     return retval;
  537. }
  538.  
  539.  
  540. /* ---------------------- Text_userItem_proc ------------------------- */
  541. static pascal void  Text_userItem_proc( WindowPtr the_window, short item_num )
  542. {
  543.     Handle        item_h;
  544.     Rect        item_box;
  545.     short        item_type;
  546.     TEHandle    the_text;
  547.         
  548.     CKPT( "Text_userItem_proc");
  549.     the_text = (TEHandle) GetWRefCon( the_window );
  550.     item_box = (**the_text).viewRect;
  551. #if USE_PICTS
  552.     ((help_ptr) the_window)->high_defer_flag = true;
  553. #endif
  554.     TEUpdate( &item_box, the_text );
  555.     
  556. #if USE_PICTS
  557.     Draw_picts( the_window, &item_box );
  558.     Do_deferred_hilites( (help_ptr) the_window, &item_box );
  559. #endif
  560.  
  561.     /*
  562.         Get the it